package com.template.utils;

import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.UUID;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 注解开发工具类
 * <p>
 * 全局静态方法调用，不需要实例化对象
 */
public class AnnotationUtils {

    /**
     * 扫描根路径{@value}
     */
    private String rootPath;
    /**
     * 类名列表{@value}
     */
    private final List<String> nameList = new ArrayList<>();
    /**
     * 注解列表{@value}
     */
    private final List<Class<Annotation>> annotationList = new ArrayList<>();
    /**
     * 类表{注解名: 类}{@value}
     */
    private final Map<String, List<Class<?>>> classPool = new HashMap<>();
    /**
     * 接口表{注解名: 接口}{@value}
     */
    private final Map<String, List<Class<?>>> interfacePool = new HashMap<>();
    /**
     * 枚举类表{注解名: 枚举类}{@value}
     */
    private final Map<String, List<Class<?>>> enumPool = new HashMap<>();
    /**
     * 属性表{注解名: {类名: 属性列表}}{@value}
     */
    private final Map<String, Map<String, List<Field>>> fieldPool = new HashMap<>();
    /**
     * 方法表{注解名: {类名: 方法列表}}{@value}
     */
    private final Map<String, Map<String, List<Method>>> methodPool = new HashMap<>();

    /**
     * 程序加载时自动调用初始化
     */
    public AnnotationUtils(Class<?> clazz) throws Exception {
        init(clazz);
    }

    /**
     * AnnotationUtils初始化
     *
     * @throws Exception
     */
    private void init(Class<?> clazz) throws Exception {
        System.out.println("----------------AnnotationUtils init begin----------------");
        // 获取class根路径
        String runModel = clazz.getResource("").getProtocol();
        System.out.println(clazz.getProtectionDomain());
        System.out.println("======");
        System.out.println(clazz.getProtectionDomain().getCodeSource());
        System.out.println("======");
        System.out.println(clazz.getProtectionDomain().getCodeSource().getLocation());
        System.out.println("======");
        System.out.println(clazz.getProtectionDomain().getCodeSource().getLocation().getPath());
        // 判断运行模式，扫描包获取所有class的全限定类名，并保存在nameList中
        if ("file".equals(runModel)) {
            rootPath = clazz.getResource("/").toString().replace("file:/", "").replace("\\", "/");
            scanPackageInFile(new File(rootPath));
        } else if ("jar".equals(runModel)) {
            rootPath = clazz.getProtectionDomain().getCodeSource().getLocation().getPath();
            scanPackageInJar();
        } else {
            System.out.println("----------------AnnotationUtils init failure----------------");
            return;
        }
        // 根据注解和类对所有class分类
        sortClass();
        System.out.println("----------------AnnotationUtils init finished----------------");
    }

    /**
     * 扫描jar包，获取所有class的全限定类名
     *
     * @throws IOException
     */
    private void scanPackageInJar() throws IOException {
        JarFile jarFile = new JarFile(new File(rootPath));
        Enumeration<JarEntry> entrys = jarFile.entries();
        while (entrys.hasMoreElements()) {
            JarEntry jar = entrys.nextElement();
            String name = jar.getName();
            if (name.endsWith(".class")) {
                nameList.add(name.replace("\\", "/").replace(".class", "").replace("/", "."));
            }
        }
        jarFile.close();
    }

    /**
     * 递归扫描包，获取所有class的全限定类名
     *
     * @param nodeFile 递归节点文件
     * @throws Exception
     */
    private void scanPackageInFile(File nodeFile) throws Exception {
        File[] filesList = nodeFile.listFiles();
        for (File file : filesList) {
            if (file.isDirectory()) {
                scanPackageInFile(file);
            } else {
                if (file.getAbsolutePath().endsWith(".class")) {
                    // 将class路径转换为全限定类名
                    nameList.add(file.getAbsolutePath().replace("\\", "/").replace(rootPath, "").replace(".class", "")
                            .replace("/", "."));
                }
            }
        }
    }

    /**
     * 根据注解和类对所有class分类
     *
     * @throws Exception
     */
    private void sortClass() throws Exception {
        // 获取所有注解类并保存
        for (String className : nameList) {
            Console.log("className ==> ", className);
            Class<?> clazz = Class.forName(className);
            if (clazz.isAnnotation()) {
                System.out.println("[AnnotationUtils] Scanning annotation: " + className);
                Class<Annotation> annotation = (Class<Annotation>) clazz;
                annotationList.add(annotation);
            }
        }

        // 遍历所有类
        for (String className : nameList) {
            Class<?> clazz = Class.forName(className);
            // 跳过所有注解类
            if (clazz.isAnnotation()) {
                continue;
            }
            // 获取当前遍历类的所有属性
            Field[] fields = clazz.getFields();
            // 获取当前遍历类的所有方法
            Method[] methods = clazz.getMethods();
            // 遍历所有注解
            for (Class<Annotation> annotation : annotationList) {
                String annotationName = annotation.getName();
                // 当前遍历类拥有当前遍历注解，则根据注解和当前遍历类的类型进行分类保存
                if (null != clazz.getAnnotation(annotation)) {
                    if (clazz.isInterface()) {
                        System.out.println("[AnnotationUtils] Scanning class(interface): " + className);
                        putPool(clazz, annotationName, interfacePool);
                    } else if (clazz.isEnum()) {
                        System.out.println("[AnnotationUtils] Scanning class(enum): " + className);
                        putPool(clazz, annotationName, enumPool);
                    } else {
                        System.out.println("[AnnotationUtils] Scanning class: " + className);
                        putPool(clazz, annotationName, classPool);
                    }
                }
                // 遍历当前遍历类的所有属性
                for (Field field : fields) {
                    // 当前遍历属性拥有当前遍历注解，则根据注解和当前遍历属性的所属类进行分类保存
                    if (null != field.getAnnotation(annotation)) {
                        Map<String, List<Field>> map;
                        List<Field> list;
                        if (fieldPool.containsKey(annotationName)) {
                            map = fieldPool.get(annotationName);
                            if (map.containsKey(className)) {
                                list = map.get(className);
                            } else {
                                list = new ArrayList<>();
                            }
                        } else {
                            map = new HashMap<>();
                            list = new ArrayList<>();
                        }
                        list.add(field);
                        map.put(className, list);
                        fieldPool.put(annotationName, map);
                    }
                }
                // 遍历当前遍历类的所有方法
                for (Method method : methods) {
                    // 当前遍历属性拥有当前遍历注解，则根据注解和当前遍历属性的所属类进行分类保存
                    if (null != method.getAnnotation(annotation)) {
                        Map<String, List<Method>> map;
                        List<Method> list;
                        if (methodPool.containsKey(annotationName)) {
                            map = methodPool.get(annotationName);
                            if (map.containsKey(className)) {
                                list = map.get(className);
                            } else {
                                list = new ArrayList<>();
                            }
                        } else {
                            map = new HashMap<>();
                            list = new ArrayList<>();
                        }
                        list.add(method);
                        map.put(className, list);
                        methodPool.put(annotationName, map);
                    }
                }
            }
        }
    }

    private void putPool(Class<?> clazz, String annotationName, Map<String, List<Class<?>>> interfacePool) {
        List<Class<?>> list;
        if (interfacePool.containsKey(annotationName)) {
            list = interfacePool.get(annotationName);
        } else {
            list = new ArrayList<Class<?>>();
        }
        list.add(clazz);
        interfacePool.put(annotationName, list);
    }

    /**
     * 根据注解查询类
     *
     * @param annotation 所查询的注解类
     * @return 所有拥有所查询注解的类的ArrayList，如果未查询到结果或所传入类不是一个注解，则返回{@code null}
     */
    public List<Class<?>> getInstanceByAnnotation(Class<?> annotation) {
        // 判断传入类是否为注解
        return getInstanceByAnnotation(annotation, classPool);
    }

    /**
     * 根据注解查询接口
     *
     * @param annotation 所查询的注解类
     * @return 所有拥有所查询注解的接口的ArrayList，如果未查询到结果或所传入类不是一个注解，则返回{@code null}
     */
    public List<Class<?>> getInterfacesByAnnotation(Class<?> annotation) {
        // 判断传入类是否为注解
        return getInstanceByAnnotation(annotation, interfacePool);
    }

    /**
     * 根据注解查询枚举类
     *
     * @param annotation 所查询的注解类
     * @return 所有拥有所查询注解的枚举类的ArrayList，如果未查询到结果或所传入类不是一个注解，则返回{@code null}
     */
    public List<Class<?>> getEnumsByAnnotation(Class<?> annotation) {
        // 判断传入类是否为注解
        return getInstanceByAnnotation(annotation, enumPool);
    }


    private List<Class<?>> getInstanceByAnnotation(Class<?> annotation, Map<String, List<Class<?>>> classPool) {
        if (!annotation.isAnnotation()) {
            return null;
        }
        String annotationName = annotation.getName();
        List<Class<?>> result = null;
        if (classPool.containsKey(annotationName)) {
            result = classPool.get(annotationName);
        }
        return result;
    }

    /**
     * 根据注解查询属性，并根据全限定类名分类
     *
     * @param annotation 所查询的注解类
     * @return 所有拥有所查询注解的属性的HashMap{全限定类名:
     * 属性的ArrayList}，如果未查询到结果或所传入类不是一个注解，则返回{@code null}
     */
    public Map<String, List<Field>> getFieldsByAnnotationSortByClass(Class<?> annotation) {
        // 判断传入类是否为注解
        if (!annotation.isAnnotation()) {
            return null;
        }
        String annotationName = annotation.getName();
        Map<String, List<Field>> result = null;
        if (fieldPool.containsKey(annotationName)) {
            result = fieldPool.get(annotationName);
        }
        return result;
    }

    /**
     * 根据注解查询方法，并根据全限定类名分类
     *
     * @param annotation 所查询的注解类
     * @return 所有拥有所查询注解的方法的HashMap{全限定类名:
     * 方法的ArrayList}，如果未查询到结果或所传入类不是一个注解，则返回{@code null}
     */
    public Map<String, List<Method>> getMethodsByAnnotationSortByClass(Class<?> annotation) {
        // 判断传入类是否为注解
        if (!annotation.isAnnotation()) {
            return null;
        }
        String annotationName = annotation.getName();
        Map<String, List<Method>> result = null;
        if (methodPool.containsKey(annotationName)) {
            result = methodPool.get(annotationName);
        }
        return result;
    }

    /**
     * 根据注解查询属性
     *
     * @param annotation 所查询的注解类
     * @return 所有拥有所查询注解的属性的ArrayList，如果未查询到结果或所传入类不是一个注解，则返回{@code null}
     */
    public List<Field> getFieldsByAnnotation(Class<?> annotation) {
        // 获取根据全限定类名分类的所有拥有所查询注解的属性的HashMap，如果未查询到结果，则返回null
        Map<String, List<Field>> temp = getFieldsByAnnotationSortByClass(annotation);
        if (null == temp) {
            return null;
        }
        // 将所有查询到的属性整合为一个ArrayList
        List<Field> result = new ArrayList<>();
        for (List<Field> list : temp.values()) {
            result.addAll(list);
        }
        return result;
    }

    /**
     * 根据注解查询方法
     *
     * @param annotation 所查询的注解类
     * @return 所有拥有所查询注解的方法的ArrayList，如果未查询到结果或所传入类不是一个注解，则返回{@code null}
     */
    public List<Method> getMethodsByAnnotation(Class<?> annotation) {
        // 获取根据全限定类名分类的所有拥有所查询注解的方法的HashMap，如果未查询到结果，则返回null
        Map<String, List<Method>> temp = getMethodsByAnnotationSortByClass(annotation);
        if (null == temp) {
            return null;
        }
        // 将所有查询到的方法整合为一个ArrayList
        List<Method> result = new ArrayList<>();
        for (List<Method> list : temp.values()) {
            result.addAll(list);
        }
        return result;
    }

    /**
     * 根据注解和类查询属性
     *
     * @param annotation 所查询的注解类
     * @param clazz      所查询的类
     * @return 所有查询类下所有拥有所查询注解的属性的ArrayList，如果未查询到结果或所传入类不是一个注解，则返回{@code null}
     */
    public List<Field> getFieldByAnnotationAndClass(Class<?> annotation, Class<?> clazz) {
        // 判断传入类是否为注解
        if (!annotation.isAnnotation()) {
            return null;
        }
        String annotationName = annotation.getName();
        String className = clazz.getName();
        List<Field> result = null;
        if (fieldPool.containsKey(annotationName)) {
            Map<String, List<Field>> map = fieldPool.get(annotationName);
            if (map.containsKey(className)) {
                result = map.get(className);
            }
        }
        return result;
    }

    /**
     * 根据注解和类查询方法
     *
     * @param annotation 所查询的注解类
     * @param clazz      所查询的类
     * @return 所有查询类下所有拥有所查询注解的方法的ArrayList，如果未查询到结果或所传入类不是一个注解，则返回{@code null}
     */
    public List<Method> getMethodByAnnotationAndClass(Class<?> annotation, Class<?> clazz) {
        // 判断传入类是否为注解
        if (!annotation.isAnnotation()) {
            return null;
        }
        String annotationName = annotation.getName();
        String className = clazz.getName();
        List<Method> result = null;
        if (methodPool.containsKey(annotationName)) {
            Map<String, List<Method>> map = methodPool.get(annotationName);
            if (map.containsKey(className)) {
                result = map.get(className);
            }
        }
        return result;
    }
}

